//
//  Move.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/10/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Move.h"
// General Move

void Move::init(MTGen* eng, Lattice * lat) {
	eng_ = eng;
	lat_ = lat;
	zeroOne_.init(eng, UniRealParams(0, 1));
}
bool Move::accept(double p) {
	if (p > 1)
		return true;
	double draw = zeroOne_.draw();
	if (p > draw)
		return true;
	else
		return false;
}
bool Move::checkMove() {
	int totalBonds;
	return lat_->testConfiguration(totalBonds);
}
// Shift Move

void ShiftMove::init(MTGen *eng, Lattice *lat, QmcParameters &qmcParameters,
		BoltzWeightType boltzRatio) {
	static_cast<Move *>(this)->init(eng, lat);
	chooseMove_.init(eng, UniIntParams(0, 2 * 2 * DIM - 1));
	setCoupeling(boltzRatio);
}

void ShiftMove::setCoupeling(BoltzWeightType boltzRatio) {
	RemoveRatio_[0] = 0.;
	for (int j = 0; j < MAX_BOND; j++) {
		CreateRatio_[j] = boltzRatio[j] ;
		if (j != (MAX_BOND - 1))
			RemoveRatio_[j + 1] = 1. / (boltzRatio[j]);
	}
}

bool ShiftMove::makeMove() {
// Get Ira
	Lattice::SiteIter ira = lat_->getIra();
	// Choose Move
	int move = chooseMove_.draw();
	// Create Or Remove
	CreateRemove createRemove = (move < 2 * DIM) ? CREATE : REMOVE;
	// Direction
	int dir = move % (2 * DIM);
	Lattice::SiteIter nSite = ira->nSiteArray_[dir];
	Site::XYBondArray &bondArray = ira->getBondArray();
	int &bondVal = getBondVal(bondArray, dir, createRemove);
	// Create Move
	if (createRemove == CREATE) {
		// Annihilate Worm
		if (nSite == lat_->getMasha()) {
			double p = 1. / double(bondVal + 1);
			if (this->accept(p)) {
				bondVal++;
				// lat_->setIra(nSite); (needed??)
				lat_->sectorType_ = ZSECTOR;
				return true;
			}
			// Create New Bond
		} else {
			// Get Total Bonds
			int &totalBonds = nSite->getTotalBonds();
			double p = (CreateRatio_[totalBonds]) / double(bondVal + 1);
			if (this->accept(p)) {
				bondVal++;
				totalBonds++;
				lat_->setIra(nSite);
				assert(this->checkMove());
				return true;
			}
		}
	} else if (bondVal != 0) {
		// Annihilate Worm
		if (nSite == lat_->getMasha()) {
			// Get Total Bonds Ira
			int &totalBondsIra = ira->getTotalBonds();
			// Get Total Bonds Masha
			int &totalBondsMasha = nSite->getTotalBonds();
			double removeIra = RemoveRatio_[totalBondsIra];
			double removeMasha = RemoveRatio_[totalBondsMasha];
			double p =  removeIra * removeMasha * double(bondVal);
			if (this->accept(p)) {
				bondVal--;
				assert(bondVal < MAX_BOND);
				totalBondsIra--;
				totalBondsMasha--;
				lat_->sectorType_ = ZSECTOR;
				// lat_->setIra(nSite); (Needed?)
				return true;
			}
		}
		// Remove Bond
		else {
			// Get Total Bonds
			int &totalBonds = ira->getTotalBonds();
			double p = (RemoveRatio_[totalBonds]) * double(bondVal);
			if (this->accept(p)) {
				bondVal--;
				assert(bondVal < MAX_BOND);
				totalBonds--;
				lat_->setIra(nSite);
				assert(this->checkMove());
				return true;
			}
		}
	}
	return false;
}
int& ShiftMove::getBondVal(boost::array<XYBondType*, 2 * DIM> &bondArray,
		int dir, CreateRemove createRemove) {
	if (dir < DIM) {
		if (createRemove == CREATE)
			return (*(bondArray)[dir])[0];
		else
			return (*(bondArray)[dir])[1];
	} else {
		if (createRemove == CREATE)
			return (*(bondArray)[dir])[1];
		else
			return (*(bondArray)[dir])[0];

	}
}

// Create Pair Move
void CreatePairMove::init(MTGen *eng, Lattice *lat,
		QmcParameters &qmcParameters_, BoltzWeightType boltzRatio) {
	static_cast<Move *>(this)->init(eng, lat);
	chooseSite_.init(eng, UniIntParams(0, lat->getNumOfSites() - 1));
	chooseMove_.init(eng, UniIntParams(0, 2 * 2 * DIM - 1));
	boltzRatio_ = boltzRatio;
}

bool CreatePairMove::makeMove() {
	// JumpSite
	int jumpSite = chooseSite_.draw();
	Lattice::SiteIter jump = lat_->getSite(jumpSite);
	// Move
	int move = chooseMove_.draw();
	// Create Or Remove
	CreateRemove createRemove = (move < 2 * DIM) ? CREATE : REMOVE;
	// Direction
	int dir = move % (2 * DIM);
	Lattice::SiteIter nSite = jump->nSiteArray_[dir];
	BondArray &bondArray = jump->getBondArray();
	int &bondVal = getBondVal(bondArray, dir, createRemove);
	// Create Move
	if (createRemove == CREATE) {
		// Get Total Bonds
		// Get Total Bonds Ira
		int &totalBondsMasha = jump->getTotalBonds();
		// Get Total Bonds Masha
		int &totalBondsIra = nSite->getTotalBonds();
		double addIra = boltzRatio_[totalBondsIra];
		double addMasha = boltzRatio_[totalBondsMasha];
		double p =  addIra * addMasha / double(bondVal + 1);
		if (this->accept(p)) {
			bondVal++;
			totalBondsIra++;
			totalBondsMasha++;
			lat_->setIra(nSite);
			lat_->setMasha(jump);
			lat_->sectorType_ = GXY;
			return true;
			assert(this->checkMove());
		}
	} else {
		double p =  double(bondVal);
		if (this->accept(p)) {
			bondVal--;
			lat_->setIra(nSite);
			lat_->setMasha(jump);
			lat_->sectorType_ = GXY;
			return true;
			assert(this->checkMove());
		}
	}
	return false;
}
void CreatePairMove::setCoupeling(BoltzWeightType boltzRatio) {
	boltzRatio_ = boltzRatio;
}
int& CreatePairMove::getBondVal(boost::array<XYBondType*, 2 * DIM> &bondArray,
		int dir, CreateRemove createRemove) {
	if (dir < DIM) {
		if (createRemove == CREATE)
			return (*(bondArray)[dir])[0];
		else
			return (*(bondArray)[dir])[1];
	} else {
		if (createRemove == CREATE)
			return (*(bondArray)[dir])[1];
		else
			return (*(bondArray)[dir])[0];

	}
}
